package com.startx.core.netty.handler;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import org.apache.log4j.Logger;

import com.startx.core.config.holder.ConfigHolder;
import com.startx.core.mvc.define.ResponseType;
import com.startx.core.mvc.factory.MappingFactory;
import com.startx.core.netty.output.http.JsonOutput;
import com.startx.core.netty.output.http.Output;
import com.startx.core.netty.output.http.XmlOutput;
import com.startx.core.system.config.StartxConfig;
import com.startx.core.system.constants.Constants;
import com.startx.core.system.model.AccessTarget;
import com.startx.core.system.param.HttpArgs;
import com.startx.core.tools.JsonTool;
import com.startx.core.tools.xml.XmlReader;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;

public class HttpHandler extends ChannelInboundHandlerAdapter {

	private static final Logger Log = Logger.getLogger(HttpHandler.class);
	private StartxConfig config = ConfigHolder.getConfig();

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		FullHttpRequest req = (FullHttpRequest) msg;
		handleHttpRequest(ctx, req);
	}

	/**
	 * 解析http请求
	 */
	public void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {

		if (request.decoderResult().isFailure()) {
			Log.error("一次奇怪的访问， IP：" + ctx.channel().remoteAddress() + "... ");
			ctx.close();
			return;
		}
		//GET POST ...
		AccessTarget target = getTarget(ctx, request);
		
		if(Objects.isNull(target)) {
			Output.status(ctx, HttpResponseStatus.NOT_FOUND);
			return;
		}
		
		ResponseType type = target.getType();
		
		HttpArgs args = new HttpArgs();
		args.setDomain(getDomain(ctx));
		args.setHeaders(getHeader(request));
		args.setIp(getIp(ctx));
		args.setParams(getParams(request));
		// 判断是否需要解析body
		Method method = target.getMethod();
		Class<?>[] parameterTypes = method.getParameterTypes();
		Object[] params = new Object[parameterTypes.length];
		
		//传参，可以自行扩展
		for(int i=0;i<parameterTypes.length;i++) {
			Class<?> clz = parameterTypes[i];
			if(clz.equals(HttpArgs.class)) {
				params[i] = args;
			} else if(clz.equals(byte[].class)) {
				params[i] = getBody(request);
			} else if(clz.equals(ChannelHandlerContext.class)) {
				params[i] = ctx;
			} else {
				if(type == ResponseType.JSON && !clz.equals(Map.class)) {
					params[i] = JsonTool.json2obj(
						new String(getBody(request),"UTF-8"), clz);
				} else if(type == ResponseType.JSON && clz.equals(Map.class)) {
					params[i] = JsonTool.json2map(
							new String(getBody(request),"UTF-8"));
				} else if(type == ResponseType.XML 
						&& clz.equals(Map.class)) {
					params[i] = XmlReader.parseXml(
						new String(getBody(request),"UTF-8"));
				}
			}
		}
		
		// 调用方法参数
		Object output = method.invoke(target.getObj(),params);
		// 根据类型写返回值
		ifReturnValueThenOutput(ctx, type, output);
		// 检测链接是否关闭
		ifIsOpenThenClose(ctx);
	}

	/**
	 * 获得调用对象
	 * @param ctx
	 * @param request
	 * @return
	 */
	private AccessTarget getTarget(ChannelHandlerContext ctx, FullHttpRequest request) {
		String httpMethod = request.method().name();
		String uri = getURI(request);
		String endPoint = config.getEndPoint();
		
		if(!Objects.isNull(endPoint) && !uri.startsWith(endPoint)) {
			return null;
		}
		
		if(!endPoint.equals(Constants.ROOT_DIR)) {
			uri = uri.replace(endPoint, Constants.EMPTY_STRING);
		}
		
		AccessTarget target = 
				MappingFactory.getAccessTarget(httpMethod+Constants.UNDER_LINE+uri);
		
		return target;
	}

	/**
	 * 获取header参数
	 */
	public Map<String, String> getHeader(FullHttpRequest request) {
		Map<String, String> headers = new HashMap<String, String>();

		List<Map.Entry<String, String>> entryList = request.headers().entries();
		for (Map.Entry<String, String> entry : entryList) {
			String key = entry.getKey();
			headers.put(key, entry.getValue());
		}

		return headers;
	}

	/**
	 * 解析body
	 */
	public byte[] getBody(FullHttpRequest request) {
		ByteBuf buf = request.content();

		byte[] body = new byte[buf.readableBytes()];

		buf.getBytes(0, body);
		buf.release();

		return body;
	}

	/**
	 * Get获取uri参数
	 */
	public Map<String, String> getParams(FullHttpRequest request) {
		Map<String, String> params = new HashMap<String, String>();

		QueryStringDecoder decoder = new QueryStringDecoder(request.uri());

		Map<String, List<String>> parame = decoder.parameters();
		for (Entry<String, List<String>> entry : parame.entrySet()) {
			params.put(entry.getKey(), entry.getValue().get(0));
		}

		return params;
	}

	/**
	 * 输出数据
	 */
	public void ifReturnValueThenOutput(ChannelHandlerContext ctx, ResponseType type, Object output)
			throws UnsupportedEncodingException {
		if (output != null) {
			if (type == ResponseType.JSON) {
				JsonOutput.object(ctx, HttpResponseStatus.OK, output);
			} else if (type == ResponseType.XML) {
				XmlOutput.object(ctx, HttpResponseStatus.OK, output);
			}
		}
	}

	/**
	 * 获取客户端IpAddress
	 */
	public String getIp(ChannelHandlerContext ctx) {
		return ctx.channel().remoteAddress().toString().replaceAll(Constants.ROOT_DIR, Constants.EMPTY_STRING)
				.split(Constants.COLON)[0];
	}

	/**
	 * 获取请求Domain
	 */
	public String getDomain(ChannelHandlerContext ctx) {
		return ctx.channel().localAddress().toString().replaceAll(Constants.ROOT_DIR, Constants.EMPTY_STRING);
	}

	/**
	 * 获取请求URI
	 */
	public String getURI(FullHttpRequest request) {
		String requestUri = request.uri();

		if (requestUri.indexOf(Constants.PARAM_SPLIT) != -1) {
			requestUri = requestUri.substring(0, request.uri().indexOf(Constants.PARAM_SPLIT));
		}

		return requestUri;
	}

	/**
	 * 如果链接没有关闭，则执行关闭
	 */
	public void ifIsOpenThenClose(ChannelHandlerContext ctx) {
		if (ctx.channel().isOpen()) {
			Output.status(ctx, HttpResponseStatus.OK);
		}
	}

	/**
	 * 异常处理
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		Output.status(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
	}
}
